# Build Qt apps with the autotools (Autoconf/Automake).
# M4 macros.
# This file is part of AutoTroll.
# Copyright (C) 2006  Benoit Sigoure <benoit.sigoure@lrde.epita.fr>
#
# AutoTroll is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
# USA.

 # ------------- #
 # DOCUMENTATION #
 # ------------- #

# Disclaimer: Never tested with anything else than Qt 4.2! Feedback welcome.
# Simply invoke AT_WITH_QT in your configure.ac. AT_WITH_QT can take
# arguments which are documented in depth below. The default arguments are
# equivalent to the default .pro file generated by qmake.
#
# Invoking AT_WITH_QT will do the following:
#  - Add a --with-qt option to your configure
#  - Find qmake, moc and uic and save them in the make variables $(QMAKE),
#    $(MOC), $(UIC).
#  - Save the path to Qt in $(QT_PATH)
#  - Find the flags to use Qt, that is:
#     * $(QT_DEFINES): -D's defined by qmake.
#     * $(QT_CFLAGS): CFLAGS as defined by qmake (C?!)
#     * $(QT_CXXFLAGS): CXXFLAGS as defined by qmake.
#     * $(QT_INCPATH): -I's defined by qmake.
#     * $(QT_CPPFLAGS): Same as $(QT_DEFINES) + $(QT_INCPATH)
#     * $(QT_LFLAGS): LFLAGS defined by qmake.
#     * $(QT_LDFLAGS): Same thing as $(QT_LFLAGS).
#     * $(QT_LIBS): LIBS defined by qmake.
#
# You *MUST* invoke $(MOC) and/or $(UIC) where necessary. AutoTroll provides
# you with Makerules to ease this, here is a sample Makefile.am to use with
# AutoTroll which builds the code given in the chapter 7 of the Qt Tutorial:
# http://doc.trolltech.com/4.2/tutorial-t7.html
#
# -------------------------------------------------------------------------
# include $(top_srcdir)/build-aux/autotroll.mk
#
# ACLOCAL_AMFLAGS = -I build-aux
#
# bin_PROGRAMS = lcdrange
# lcdrange_SOURCES =  $(BUILT_SOURCES) lcdrange.cpp lcdrange.h main.cpp
# lcdrange_CXXFLAGS = $(QT_CXXFLAGS) $(AM_CXXFLAGS)
# lcdrange_CPPFLAGS = $(QT_CPPFLAGS) $(AM_CPPFLAGS)
# lcdrange_LDFLAGS  = $(QT_LDFLAGS) $(LDFLAGS)
# lcdrange_LDADD    = $(QT_LIBS) $(LDADD)
#
# BUILT_SOURCES = lcdrange.moc.cpp
# -------------------------------------------------------------------------
#
# Note that your MOC, UIC and QRC files *MUST* be listed manually in
# BUILT_SOURCES. If you name them properly (eg: .moc.cc, .qrc.cc, .ui.cc -- of
# course you can use .cpp or .cxx or .C rather than .cc) AutoTroll will build
# them automagically for you (using implicit rules defined in autotroll.mk).

m4_pattern_forbid([^AT_])dnl
m4_pattern_forbid([^_AT_])dnl

# AT_WITH_QT([QT_modules], [QT_config], [QT_misc])
# ------------------------------------------------
# Enable Qt support and add an option --with-qt to the configure script.
#
# The QT_modules argument is optional and defines extra modules to enable or
# disable (it's equivalent to the QT variable in .pro files). Modules can be
# specified as follows:
#
# AT_WITH_QT   => No argument -> No QT value.
#                                Qmake sets it to "core gui" by default.
# AT_WITH_QT([xml])   => QT += xml
# AT_WITH_QT([+xml])  => QT += xml
# AT_WITH_QT([-gui])  => QT -= gui
# AT_WITH_QT([xml -gui +sql svg])  => QT += xml sql svg
#                                     QT -= gui
#
# The QT_config argument is also optional and follows the same convention as
# QT_modules. Instead of changing the QT variable, it changes the CONFIG
# variable, which is used to tweak configuration and compiler options.
#
# The last argument, QT_misc (also optional) will be copied as-is the .pro
# file used to guess how to compile Qt apps. You may use it to further tweak
# the build process of Qt apps if tweaking the QT or CONFIG variables isn't
# enough for you.
AC_DEFUN([AT_WITH_QT],
[ AC_REQUIRE([AC_CANONICAL_HOST])
  AC_REQUIRE([AC_CANONICAL_BUILD])
  AC_REQUIRE([AC_PROG_CXX])

  test x"$TROLL" != x && echo 'ViM rox emacs.'

dnl Memo: AC_ARG_WITH(package, help-string, [if-given], [if-not-given])
  AC_ARG_WITH([qt],
              [AS_HELP_STRING([--with-qt],
                 [Path to Qt @<:@Look in PATH and /usr/local/Trolltech@:>@])],
              [QT_PATH=$withval], [QT_PATH=])

  # Find Qt.
  if test -d /usr/local/Trolltech; then
    # Try to find the latest version.
    tmp_qt_paths=`echo /usr/local/Trolltech/*/bin | tr ' ' '\n' | sort -nr \
                                              | xargs | sed 's/  */:/g'`
  fi

  # Find qmake.
  if test -z "$QMAKE"; then
    AC_PATH_PROGS([QMAKE], [qmake], [missing], [$QT_DIR:$QT_PATH:$PATH:$tmp_qt_paths])
  else
    AC_MSG_CHECKING([for the QMAKE to use with Qt])
    AC_MSG_RESULT([$QMAKE])
  fi
  if test x"$QMAKE" = xmissing; then
    AC_MSG_ERROR([Cannot find qmake in your PATH. Try using --with-qt.])
  fi

  # Find moc (Meta Object Compiler).
  if test -z "$MOC"; then
    AC_PATH_PROGS([MOC], [moc], [missing], [$QT_PATH:$PATH:$tmp_qt_paths])
  else
    AC_MSG_CHECKING([for the MOC to use with Qt])
    AC_MSG_RESULT([$MOC])
  fi
  if test x"$MOC" = xmissing; then
    AC_MSG_ERROR([Cannot find moc (Meta Object Compiler) in your PATH. Try using --with-qt.])
  fi

  # Find uic (User Interface Compiler).
  if test -z "$UIC"; then
    AC_PATH_PROGS([UIC], [uic], [missing], [$QT_PATH:$PATH:$tmp_qt_paths])
  else
    AC_MSG_CHECKING([for the UIC to use with Qt])
    AC_MSG_RESULT([$UIC])
  fi
  if test x"$UIC" = xmissing; then
    AC_MSG_ERROR([Cannot find uic (User Interface Compiler) in your PATH. Try using --with-qt.])
  fi

  # Find rcc (Qt Resource Compiler).
  if test -z "$RCC"; then
    AC_PATH_PROGS([RCC], [rcc], [false], [$QT_PATH:$PATH:$tmp_qt_paths])
  else
    AC_MSG_CHECKING([for the rcc to use with Qt])
    AC_MSG_RESULT([$RCC])
  fi
  if test x"$RCC" = xfalse; then
    AC_MSG_WARN([Cannot find rcc (Qt Resource Compiler) in your PATH. Try using --with-qt.])
  fi

  # If we don't know the path to Qt, guess it from the path to qmake.
  if test x"$QT_PATH" = x; then
    QT_PATH=`dirname "$QMAKE"`
  fi
  if test x"$QT_PATH" = x; then
    AC_MSG_ERROR([Cannot find the path to your Qt install. Use --with-qt.])
  fi
  AC_SUBST([QT_PATH])
  AC_MSG_CHECKING([for the QT_PATH to use with Qt])
  AC_MSG_RESULT([$QT_PATH])

  # Get ready to build a test-app with Qt.

  # Look for a writable temporary directory.
  AC_ARG_VAR([TMPDIR], [A temporary directory with write access @<:@/tmp@:>@])
  if test x"$TMPDIR" = x || test ! -d "$TMPDIR" || test ! -w "$TMPDIR"; then
    echo "$as_me:$LINENO: no TMPDIR or bad TMPDIR ($TMPDIR)" \
      >&AS_MESSAGE_LOG_FD
    for i in /tmp /var/tmp; do
      if test -d "$i" && test -w "$i"; then
        TMPDIR=$i
        export TMPDIR
        echo "$as_me:$LINENO: setting TMPDIR=$TMPDIR" >&AS_MESSAGE_LOG_FD
        break
      fi
    done
  fi

  # Kludge!!  QMake has a very strange behavior.  For instance, if you
  # install Qt under your $HOME and run QMake somewhere else under your
  # $HOME, it will try to be clever and produce Makefiles with relative
  # include paths.  In order to avoid this, we will test QMake from a
  # temporary directory (usually /tmp).  Note that this problem was only
  # observed with Qt 4.
  my_configure_pwd=`pwd`
  my_tmpdir="$TMPDIR/conftest$$.dir"
  test -d "$my_tmpdir" || mkdir "$my_tmpdir"
  if test -w "$my_tmpdir" && cd "$my_tmpdir"
  then
    :
  else
    AC_MSG_ERROR([Cannot cd to or write in $my_tmpdir])
  fi
  cat >conftest.h <<_ASEOF
#include <QObject>

class Foo: public QObject
{
  Q_OBJECT;
public:
  Foo();
  ~Foo() {}
public slots:
  void setValue(int value);
signals:
  void valueChanged(int newValue);
private:
  int value_;
};
_ASEOF

  cat >conftest.cpp <<_ASEOF
#include "conftest.h"
Foo::Foo()
  : value_ (42)
{
  connect(this, SIGNAL(valueChanged(int)), this, SLOT(setValue(int)));
}

void Foo::setValue(int value)
{
  value_ = value;
}

int main()
{
  Foo f;
}
_ASEOF
  if $QMAKE -project; then :; else
    AC_MSG_ERROR([Calling $QMAKE -project failed.])
  fi

  # Find the .pro file generated by qmake.
  pro_file='conftest.dir.pro'
  test -f $pro_file || pro_file=`echo *.pro`
  if test -f "$pro_file"; then :; else
    AC_MSG_ERROR([Can't find the .pro file generated by Qmake.])
  fi

dnl Tweak the value of QT in the .pro if have been the 1st arg.
m4_ifval([$1], [_AT_TWEAK_PRO_FILE([QT], [$1])])

dnl Tweak the value of CONFIG in the .pro if have been given a 2nd arg.
m4_ifval([$2], [_AT_TWEAK_PRO_FILE([CONFIG], [$2])])

m4_ifval([$3],
[ # Add the extra-settings the user wants to set in the .pro
  echo "$3" >>"$pro_file"
])

  echo "$as_me:$LINENO: Invoking $QMAKE on $pro_file" >&AS_MESSAGE_LOG_FD
  sed 's/^/| /' "$pro_file" >&AS_MESSAGE_LOG_FD

  additional_qmake_flags=""
  case $host_os in
    darwin*)
      additional_qmake_flags="-spec macx-g++"
      ;;
  esac

  if $QMAKE ${additional_qmake_flags} ; then :; else
    AC_MSG_ERROR([Calling $QMAKE failed.])
  fi
  # Try to compile a simple Qt app.
  AC_CACHE_CHECK([whether we can build a simple Qt app], [at_cv_qt_build],
  [at_cv_qt_build=ko
  : ${MAKE=make}

  if $MAKE >&AS_MESSAGE_LOG_FD 2>&1; then
    at_cv_qt_build='ok, looks like Qt 4'
  else
    echo "$as_me:$LINENO: Build failed, trying to #include <qobject.h> \
instead" >&AS_MESSAGE_LOG_FD
    sed 's/<QObject>/<qobject.h>/' conftest.h > tmp.h && mv tmp.h conftest.h
    if $MAKE >&AS_MESSAGE_LOG_FD 2>&1; then
      at_cv_qt_build='ok, looks like Qt 3'
    else
      # Sometimes (such as on Debian) build will fail because Qt hasn't been
      # installed in debug mode and qmake tries (by default) to build apps in
      # debug mode => Try again in release mode.
      echo "$as_me:$LINENO: Build failed, trying to enforce release mode" \
            >&AS_MESSAGE_LOG_FD

      _AT_TWEAK_PRO_FILE([CONFIG], [+release])

      sed 's/<qobject.h>/<QObject>/' conftest.h > tmp.h && mv tmp.h conftest.h
      if $MAKE >&AS_MESSAGE_LOG_FD 2>&1; then
        at_cv_qt_build='ok, looks like Qt 4, release mode forced'
      else
        echo "$as_me:$LINENO: Build failed, trying to #include <qobject.h> \
instead" >&AS_MESSAGE_LOG_FD
        sed 's/<QObject>/<qobject.h>/' conftest.h > tmp.h && mv tmp.h conftest.h
        if $MAKE >&AS_MESSAGE_LOG_FD 2>&1; then
          at_cv_qt_build='ok, looks like Qt 3, release mode forced'
        else
          at_cv_qt_build=ko
          echo "$as_me:$LINENO: failed program was:" >&AS_MESSAGE_LOG_FD
          sed 's/^/| /' conftest.h >&AS_MESSAGE_LOG_FD
          echo "$as_me:$LINENO: failed program was:" >&AS_MESSAGE_LOG_FD
          sed 's/^/| /' conftest.cpp >&AS_MESSAGE_LOG_FD
        fi # if make with Qt3-style #include and release mode forced.
      fi # if make with Qt4-style #include and release mode forced.
    fi # if make with Qt3-style #include.
  fi # if make with Qt4-style #include.
  ])dnl end: AC_CACHE_CHECK(at_cv_qt_build)

  if test x"$at_cv_qt_build" = xko; then
    AC_MSG_ERROR([Cannot build a test Qt program])
  fi
  QT_VERSION_MAJOR=`echo "$at_cv_qt_build" | sed 's/^[^0-9]*//'`
  AC_SUBST([QT_VERSION_MAJOR])

  # This sed filter is applied after an expression of the form: /^FOO.*=/!d;
  # It starts by removing the beginning of the line, removing references to
  # SUBLIBS, removing unnecessary whitespaces at the beginning, and prefixes
  # all variable uses by QT_.
  qt_sed_filter='s///;
                 s/$(SUBLIBS)//g;
                 s/^ *//;
                 s/\$(\(@<:@A-Z_@:>@@<:@A-Z_@:>@*\))/$(QT_\1)/g'

  # Find the Makefile (qmake happens to generate a fake Makefile which invokes
  # a Makefile.Debug or Makefile.Release). We we have both, we'll pick the
  # Makefile.Release. The reason is that the main difference is that release
  # uses -Os and debug -g. We can override -Os by passing another -O but we
  # usually don't override -g.
  if test -f Makefile.Release; then
    at_mfile='Makefile.Release'
  else
    at_mfile='Makefile'
  fi
  if test -f $at_mfile; then :; else
    cd "$my_configure_pwd"
    AC_MSG_ERROR([Cannot find the Makefile generated by qmake.])
  fi

  # Find the DEFINES of Qt (should have been named CPPFLAGS).
  if test -z "$QT_DEFINES"; then
    AC_CACHE_CHECK([for the DEFINES to use with Qt], [at_cv_env_QT_DEFINES],
    [at_cv_env_QT_DEFINES=`sed "/^DEFINES@<:@^A-Z@:>@*=/!d;$qt_sed_filter" $at_mfile`])
    QT_DEFINES="$at_cv_env_QT_DEFINES"
  fi
  AC_SUBST([QT_DEFINES])
  AC_MSG_CHECKING([for the QT_DEFINES to use with Qt])
  AC_MSG_RESULT([$QT_DEFINES])

  # Find the CFLAGS of Qt (We can use Qt in C?!)
  if test -z "$QT_CFLAGS"; then
    AC_CACHE_CHECK([for the CFLAGS to use with Qt], [at_cv_env_QT_CFLAGS],
    [at_cv_env_QT_CFLAGS=`sed "/^CFLAGS@<:@^A-Za-z@:>@*=/!d;$qt_sed_filter" $at_mfile`])
    QT_CFLAGS="$at_cv_env_QT_CFLAGS"
  fi
  AC_SUBST([QT_CFLAGS])
  AC_MSG_CHECKING([for the QT_CFLAGS to use with Qt])
  AC_MSG_RESULT([$QT_CFLAGS])

  # Find the CXXFLAGS of Qt.
  if test -z "$QT_CXXFLAGS"; then
    AC_CACHE_CHECK([for the CXXFLAGS to use with Qt], [at_cv_env_QT_CXXFLAGS],
    [at_cv_env_QT_CXXFLAGS=`sed "/^CXXFLAGS@<:@^A-Za-z@:>@*=/!d;$qt_sed_filter" $at_mfile`])
    QT_CXXFLAGS="$at_cv_env_QT_CXXFLAGS"
  fi
  AC_SUBST([QT_CXXFLAGS])
  AC_MSG_CHECKING([for the QT_CXXFLAGS to use with Qt])
  AC_MSG_RESULT([$QT_CXXFLAGS])

  # Find the INCPATH of Qt.
  if test -z "$QT_INCPATH"; then
    AC_CACHE_CHECK([for the INCPATH to use with Qt], [at_cv_env_QT_INCPATH],
    [at_cv_env_QT_INCPATH=`sed "/^INCPATH@<:@^A-Z@:>@*=/!d;$qt_sed_filter" $at_mfile`])
    QT_INCPATH="$at_cv_env_QT_INCPATH"
  fi
  AC_SUBST([QT_INCPATH])
  AC_MSG_CHECKING([for the QT_INCPATH to use with Qt])
  AC_MSG_RESULT([$QT_INCPATH])
  
  QT_CPPFLAGS="$QT_DEFINES $QT_INCPATH"
  AC_SUBST([QT_CPPFLAGS])

  # Find the LFLAGS of Qt (Should have been named LDFLAGS)
  if test -z "$QT_LDFLAGS"; then
    AC_CACHE_CHECK([for the LDFLAGS to use with Qt], [at_cv_env_QT_LDFLAGS],
    [at_cv_env_QT_LDFLAGS=`sed "/^LFLAGS@<:@^A-Za-z@:>@*=/!d;$qt_sed_filter" $at_mfile`])
    QT_LDFLAGS="$at_cv_env_QT_LDFLAGS"
  fi
  AC_SUBST([QT_LDFLAGS])
  AC_SUBST([QT_LFLAGS], [$QT_LDFLAGS])
  AC_MSG_CHECKING([for the QT_LDFLAGS to use with Qt])
  AC_MSG_RESULT([$QT_LDFLAGS])

  AC_MSG_CHECKING([whether host operating system is Darwin])
  at_darwin="no"
  case $host_os in
    darwin*)
      at_darwin="yes"
      ;;
  esac
  AC_MSG_RESULT([$at_darwin])

  # Find the LIBS of Qt.
  if test -z "$QT_LIBS"; then
    AC_CACHE_CHECK([for the LIBS to use with Qt], [at_cv_env_QT_LIBS],
    [at_cv_env_QT_LIBS=`sed "/^LIBS@<:@^A-Za-z@:>@*=/!d;$qt_sed_filter" $at_mfile`
     if test x$at_darwin = xyes; then
       # Fix QT_LIBS: as of today Libtool (GNU Libtool 1.5.23a) doesn't handle
       # -F properly. The "bug" has been fixed on 22 October 2006
       # by Peter O'Gorman but we provide backward compatibility here.
       at_cv_env_QT_LIBS=`echo "$at_cv_env_QT_LIBS" \
                               | sed 's/^-F/-Wl,-F/;s/ -F/ -Wl,-F/g'`
     fi
    ])
    QT_LIBS="$at_cv_env_QT_LIBS"
  fi
  AC_SUBST([QT_LIBS])
  AC_MSG_CHECKING([for the QT_LIBS to use with Qt])
  AC_MSG_RESULT([$QT_LIBS])

  cd "$my_configure_pwd" || echo 'WTF!'
  rm -rf "$my_tmpdir"
])

# AT_REQUIRE_QT_VERSION(QT_version)
# ---------------------------------
# Check (using qmake) that Qt's version "matches" QT_version.
# Must be run AFTER AT_WITH_QT. Requires autoconf 2.60.
AC_DEFUN([AT_REQUIRE_QT_VERSION],
[ AC_PREREQ([2.60])
  if test x"$QMAKE" = x; then
    AC_MSG_ERROR([\$QMAKE is empty. \
Did you invoke AT@&t@_WITH_QT before AT@&t@_REQUIRE_QT_VERSION?])
  fi
  AC_CACHE_CHECK([for Qt's version], [at_cv_QT_VERSION],
  [echo "$as_me:$LINENO: Running $QMAKE --version:" >&AS_MESSAGE_LOG_FD
  $QMAKE --version >&AS_MESSAGE_LOG_FD 2>&1
  qmake_version_sed=['/^.*\([0-9]\.[0-9]\.[0-9]\).*$/!d;s//\1/']
  at_cv_QT_VERSION=`$QMAKE --version 2>&1 | sed "$qmake_version_sed"`])
  if test x"$at_cv_QT_VERSION" = x; then
    AC_MSG_ERROR([Cannot detect Qt's version.])
  fi
  AC_SUBST([QT_VERSION], [$at_cv_QT_VERSION])
  AS_VERSION_COMPARE([$QT_VERSION], [$1],
    [AC_MSG_ERROR([This package requires Qt $1 or above.])])
])

# _AT_TWEAK_PRO_FILE(QT_VAR, VALUE)
# ---------------------------
# @internal. Tweak the variable QT_VAR in the .pro.
# VALUE is an IFS-separated list of value and each value is rewritten
# as follows:
#   +value  => QT_VAR += value
#   -value  => QT_VAR -= value
#    value  => QT_VAR += value
AC_DEFUN([_AT_TWEAK_PRO_FILE],
[ # Tweak the value of $1 in the .pro file for $2.

  qt_conf=''
  for at_mod in $2; do
    at_mod=`echo "$at_mod" | sed 's/^-//; tough
                                  s/^+//; beef
                                  :ough
                                  s/^/$1 -= /;n
                                  :eef
                                  s/^/$1 += /'`
    qt_conf="$qt_conf
$at_mod"
  done
  echo "$qt_conf" | sed 1d >>"$pro_file"
])
